﻿using System.Linq;
using Borg;
using Borg.Framework;
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

[Slot("Borg.Preferences", OnPlugged = "PreferencesPlugged", OnUnplugged = "PreferencesUnplugged")]
[Plug("Startup")]
[ParamValue("ExecuteInMainThread", true, new string[] { })]
[Plug("Borg.View", OnPlugged = "ViewPlugged")]
[Plug("Borg.Tool", OnPlugged = "ToolPlugged")]
[Plug("Borg.PropertyGroup")]
[ParamValue("Name", "Console", new string[] { })]
[ParamValue("Kind", 2, new string[] { })]
[ParamValue("OrderIndex", 0.9f, new string[] { })]
[ParamValue("Icon", "console.ico", new string[] { })]
[Extension("Console", OnCreated = "OnCreated", OnReleased = "OnReleased")]
[Slot("Borg.CommandInterpreter", Multiple = false, LazyLoad = true)]
public class Console : IStartup, IFlipFormChild, ITool, IView, IPropertyGroup
{
    private SlotInfo _cmdInterpreterSlot;
    private string _cmdStart;
    private string[] _complOptions;
    private int _curComplIndex;
    private readonly Font _defaultFont = new Font("Lucida Console", 9.75f, FontStyle.Regular, GraphicsUnit.Point, 0);
    private ExtensionInfo _ext;
    private readonly ConsoleForm _form = new ConsoleForm();
    private bool _shutdownOnCloseForm = true;

    private PlugInfo _toolPlug;
    private PlugInfo _viewPlug;

    public Console()
    {
        _form.Closed += FormClosed;
        ConsoleUC.ShellTextFont = _defaultFont;
    }

    private void CompleteCommand(bool backward)
    {
        if (_complOptions == null || _complOptions.Length == 0)
            return;

        if (backward)
            _curComplIndex--;
        else
            _curComplIndex++;

        if (_curComplIndex < 0)
            _curComplIndex = _complOptions.Length - 1;
        else if (_curComplIndex == _complOptions.Length)
            _curComplIndex = 0;

        ConsoleUC.CurrentLine = _cmdStart + _complOptions[_curComplIndex];
    }

    public void Dispose()
    {
        _form.Dispose();
    }

    private void FormClosed(object sender, EventArgs e)
    {
        if (_shutdownOnCloseForm)
            ExitAction.Do(this);

        _ext.Release();
    }

    private void InterpreterStateChanged(object sender, EventArgs e)
    {
        var interpreter = (ICommandInterpreter)sender;

        if (interpreter.State != CommandState.Completed)
            return;

        interpreter.StateChanged -= InterpreterStateChanged;
        ConsoleUC.WriteLine(interpreter.Output, new object[0]);
    }

    public void OnCreated(object sender, ExtensionEventArgs args)
    {
        ConsoleUC.CommandEntered += ShellCommandEntered;
        ConsoleUC.KeyDown += ShellKeyDown;

        ConsoleUC.InitText = 
            string.Format("{0}{2}{1}{2}",
            Runtime.VersionInfo(), "Borg Runtime is launched.", Environment.NewLine);

        _ext = args.ExtensionInfo;

        _viewPlug = _ext.PlugInfos["Borg.View"];
        _cmdInterpreterSlot = _ext.SlotInfos["Borg.CommandInterpreter"];
        _toolPlug = _ext.PlugInfos["Borg.Tool"];
    }

    public void OnReleased(object sender, ExtensionEventArgs args)
    {
        _shutdownOnCloseForm = false;
        _form.Hide();
    }

    public void PreferencesPlugged(object sender, PlugEventArgs args)
    {
        var preferences = new Preferences(args);

        if (_ext.PlugInfos["Startup"].PluggedInSlots.Count > 1)
            preferences.RestoreWindow(_form);

        Font = (Font)preferences.Get("Font", Font);
        TextColor = (Color)preferences.Get("TextColor", TextColor);
        BackColor = (Color)preferences.Get("BackColor", BackColor);
    }

    public void PreferencesUnplugged(object sender, PlugEventArgs args)
    {
        var preferences = new Preferences(args);

        if (_ext.PlugInfos["Startup"].PluggedInSlots.Count > 1)
            preferences.SaveWindow(_form);

        preferences.Set("Font", Font);
        preferences.Set("TextColor", TextColor);
        preferences.Set("BackColor", BackColor);
    }

    public void ResetFont()
    {
        _form.consoleUC.ShellTextFont = _defaultFont;
    }

    public void Run()
    {
        if (_viewPlug.PlugTypeInfo.RegisteredInSlots.Count <= 0 &&
            _toolPlug.PlugTypeInfo.RegisteredInSlots.Count <= 0)
            Runtime.TaskQueue.InvokeOnEmptied(Show);
    }

    private void ShellCommandEntered(object sender, CommandEventArgs e)
    {
        if (string.IsNullOrEmpty(e.Command))
            return;

        if (Interpreter == null)
        {
            if (!Runtime.AutoRegister)
                _cmdInterpreterSlot.Register();

            SlotInfoEx.CreateAndPlug(_cmdInterpreterSlot);
            
            if (Interpreter == null)
            {
                ConsoleUC.WriteLine("  Command '{0}' not found - command interpreter is missing",
                    new object[] { e.Command });

                return;
            }
        }

        Interpreter.StateChanged += InterpreterStateChanged;
        ConsoleUC.WriteLine(Interpreter.Execute(e.Command), new object[0]);
    }

    private void ShellKeyDown(object sender, KeyEventArgs e)
    {
        if (e.KeyCode == Keys.Tab)
        {
            if (_complOptions == null)
            {
                if (Interpreter == null)
                {
                    SlotInfoEx.CreateAndPlug(_cmdInterpreterSlot);

                    if (Interpreter == null)
                        return;
                }

                _cmdStart = ConsoleUC.CurrentLine;
                _complOptions = Interpreter.GetCommandOptions(ref _cmdStart);
            }

            CompleteCommand(e.Shift);
        }
        else if ((!e.Shift && !e.Alt) && !e.Control)
        {
            _complOptions = null;
            _curComplIndex = -1;
            
            if (e.KeyCode == Keys.Escape)
            {
                ConsoleUC.CurrentLine = string.Empty;
            }
        }
    }

    public bool ShouldSerializeFont()
    {
        return _form.consoleUC.ShellTextFont != _defaultFont;
    }

    public void Show()
    {
        _form.Show();
        _form.Activate();
    }

    public void ToolPlugged(object sender, PlugEventArgs args)
    {
        _shutdownOnCloseForm = false;
        _viewPlug.Unplug();

        foreach (var slot in _toolPlug.PluggedInSlots.Where(slot => slot != args.SlotInfo))
            slot.Unplug(_toolPlug);
    }

    public void ViewPlugged(object sender, PlugEventArgs args)
    {
        if (!Runtime.IsCompositionAllowed)
            return;

        _toolPlug.Unplug();

        foreach (var slot in _viewPlug.PluggedInSlots.Where(slot => slot != args.SlotInfo))
            slot.Unplug(_viewPlug);
    }

    [Category("Console")]
    [DefaultValue(typeof(Color), "Black")]
    public Color BackColor
    {
        get { return _form.consoleUC.ShellTextBackColor; }
        set { _form.consoleUC.ShellTextBackColor = value; 
        }
    }

    public ConsoleUC ConsoleUC
    {
        get { return _form.consoleUC; }
    }

    [Browsable(false)]
    public Control Control
    {
        get { return _form.consoleUC; }
    }

    [Browsable(false)]
    public IFlipForm FlipFormParent
    {
        set { }
    }

    [Category("Console")]
    public Font Font
    {
        get { return _form.consoleUC.ShellTextFont; }
        set { _form.consoleUC.ShellTextFont = value; }
    }

    [Browsable(false)]
    public Form Form
    {
        get { return _form; }
    }

    private ICommandInterpreter Interpreter
    {
        get
        {
            return _cmdInterpreterSlot.PluggedPlugInfos.Count > 0
                ? _cmdInterpreterSlot.PluggedPlugInfos[0].ExtensionInfo.Object as ICommandInterpreter
                : null;
        }
    }

    [Category("Console")]
    public string Name
    {
        get { return "Console"; }
    }

    [Category("Console")]
    [DefaultValue(typeof(Color), "LightGray")]
    public Color TextColor
    {
        get { return _form.consoleUC.ShellTextForeColor; } 
        set { _form.consoleUC.ShellTextForeColor = value; }
    }
}

